<?php

/**
 * Authentication class.
 *
 * @version  $Id: auth.php 2 2009-10-02 23:06:43Z perfilev $
 * @package  System
 */

class Auth
{
	/**
	 * @var  string  Cookie's name.
	 */
	public static $cookie = 'token';

	/**
	 * @var  integer  Cookie's lifetime. Default is two weeks. Set to zero to disable cookies.
	 */
	public static $lifetime = 1209600;

	/**
	 * @var  array  Class instances.
	 */
	protected static $instance;

	/**
	 * Returns an instance of Auth class by the given session id.
	 *
	 * @return  Auth
	 */
	public static function getInstance()
	{
		if (null == self::$instance) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Consrtuctor.
	 *
	 * @return  Auth
	 */
	public function __construct()
	{
		if ('' === session_id()) {
			session_start();
		}
	}

	/**
	 *
	 * Generates password from value
	 *
	 * @return string
	 */
	public static function generatePassword($value)
	{
		$salt = System::random('hexdec', 8);
		return $salt . sha1($salt . $value);
	}

	/**
	 * Returns true if a user is signed in.
	 *
	 * @return boolean
	 */
	public function hasIdentity()
	{
		return false !== $this->getIdentity();
	}

	/**
	 *
	 * @return mixed
	 */
	public function getIdentity()
	{
		if (isset($_SESSION['identity']) && is_array($_SESSION['identity'])) {
			return $_SESSION['identity'];
		}
		if (self::$lifetime > 0) {
			if (isset($_COOKIE[self::$cookie])) {
				$token = explode('.', $_COOKIE[self::$cookie]);
				if (count($token) == 2 && is_numeric($token[0]) && '' !== $token[1]) {
					$user = Model_Table::instance('users')->get((int)$token[0]);
					if ($user && $user['token'] == $token[1]) {
						return $this->setupIdentity($user, true);
					}
				}
			}
		}
		return false;
	}

	/**
	 * Helper for getIdentity() and authenticate() methods.
	 *
	 * @return array
	 */
	protected function setupIdentity(array $user, $cookie = false)
	{
		$identity = array(
			'id'   => (int) $user['id'],
			'name' => $user['name'],
			'role' => $user['role']
		);
		if ($cookie && self::$lifetime > 0) {
			$token = System::random('alphanum', 32);
			setcookie(self::$cookie, $user['id'] . '.' .$token, time() + self::$lifetime, '/');
			Model_Table::instance('users')->update(array('token' => $token), (int) $user['id']);
			$identity['token'] = $token;
		}
		session_regenerate_id(true);
		return $_SESSION['identity'] = $identity;
	}

	/**
	 * Authenticates by username and password.
	 *
	 * @param  mixed    $username  String username or user database fetch result.
	 * @param  string   $password  Password.
	 * @param  boolean  $cookie    Auto sign in?
	 * @return boolean
	 */
	public function authenticate($username, $password, $cookie = false)
	{
		if (empty($password)) {
			return false;
		}
		if (is_array($username)) {
			$user = $username;
		} else {
			$users = Model_Table::instance('users');
			$users->keys['string'] = 'name';
			$user = $users->get((string)$username);
		}
		if ($user) {
			$salt = substr($user['password'], 0, 8);
			if ($salt . sha1($salt . $password) == $user['password']) {
				$this->setupIdentity($user, $cookie);
				return true;
			}
		}
		return false;
	}

	/**
	 * Sign out an user.
	 *
	 * @return void
	 */
	public function clearIdentity()
	{
		if (isset($_SESSION['identity'])) {
			unset($_SESSION['identity']);
		}
		if (isset($_COOKIE[self::$cookie])) {
			setcookie(self::$cookie, '', time() - 3600, '/');
		}
		session_regenerate_id(true);
	}

	/**
	 * Whether the requested privileges on the gevin resources are allowed to the current identity role?
	 *
	 * @param  array  $resources   Requested resoureces.
	 * @param  array  $privileges  Requested privileges on resoureces.
	 * @return boolean
	 */
	public function allowed($resources = null, $privileges = null)
	{
		if ($identity = $this->getIdentity()) {
			$role = $identity['role'];
		} else {
			$role = 'guest';
		}
		return Acl::getInstance()->allowed($role, $resources, $privileges);
	}

}
